package com.thoughtworks.bytecode2class;

import com.thoughtworks.bytecode2class.attribute.Attribute;
import com.thoughtworks.bytecode2class.attribute.CodeAttribute;
import com.thoughtworks.bytecode2class.attribute.ExceptionsAttribute;

import java.util.ArrayList;
import java.util.List;

public class MethodAndFieldRef {
    private List<AccessFlag> accessFlags;
    private String name;
    private String descriptor;
    private int attributesCount;

    private List<Attribute> attribues = new ArrayList<Attribute>();

    private ClassReader classReader;

    public MethodAndFieldRef(ClassReader classReader) {
        this.classReader = classReader;
        parseAccessFlags();
        parseName();
        parseDescriptor();
        parseAttributeCount();
        parseAttributes();
    }

    public List<Attribute> getAttribues() {
        return attribues;
    }

    private void parseAttributes() {
        for (int j = 0; j < attributesCount; j++) {
            attribues.add(Attribute.create(classReader));
        }
    }

    private void parseAttributeCount() {
        attributesCount = classReader.forwardChar();
        attribues = new ArrayList<Attribute>(attributesCount);
    }

    private void parseDescriptor() {
        int descriptorIndex = classReader.forwardChar();
        this.descriptor = classReader.getConstantPool().getUtf8Constant(descriptorIndex).getValue();
    }

    private void parseName() {
        int nameIndex = classReader.forwardChar();
        this.name = classReader.getConstantPool().getUtf8Constant(nameIndex).getValue();
    }

    private void parseAccessFlags() {
        int accessFlags = classReader.forwardChar();
        this.accessFlags = classReader.calculateAccessFlags(accessFlags);
    }

    public String getName() {
        return name;
    }

    public String getDescriptor() {
        return descriptor;
    }

    public List<AccessFlag> getAccessFlags() {
        return accessFlags;
    }

    /**
     * If method is a Interface, Abstract class or Annotation method, then return null.
     * @return CodeAttribute
     */
    public CodeAttribute getCodeAttribute() {
        return (CodeAttribute) getAttributeByName("Code");
    }

    public ExceptionsAttribute getExceptionsAttribute() {
        return (ExceptionsAttribute) getAttributeByName("Exceptions");
    }

    private Attribute getAttributeByName(String name) {
        for (Attribute attribue : attribues) {
            if (attribue.getName().equals(name)) {
                return attribue;
            }
        }
        return null;
    }
}